/******************************************************************************************************
 * Author		:	Chester Ragel
 * Date			:	01 - March - 2007
 * Description	:	Base class for context menu extenstion. This can be driven from any class to create context menus
 *					very easily	
 ******************************************************************************************************/

/*
 *  Context Menu Extention is the legal property of its developer
 *  Chester Ragel <chesterr@gmail.com> - Developer and Maintainer
 *	Sourceforge ID : chesterr
 *	Yahoo ID : chesterr1979 
 *	MSN : chesterr79@hotmail.com
 *	Gmail: chesterr@gmail.com
 *
 * If you want more information, found any bugs or have any doubts you 
 * can mail me or contact me through my yahoo id.
 * 
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#region Namespace
using System;
using System.Text;
using System.Drawing;
using System.Collections;
using System.Windows.Forms;
using System.Runtime.InteropServices;

#endregion

namespace Utils.ShellExtension.ContextMenu
{
	/// <summary>
	/// Base class for context menu extenstion. This can be driven from any class to create context menus
	/// very easily
	/// </summary>
	public abstract class BaseContextMenu : Win32.IContextMenu, Win32.IShellExtInit
	{

		#region Private Variables
		private uint drop;
		private Win32.IDataObject dataObject;
		private Hashtable hsIDCommand = new Hashtable();
		private MenuItems menus = new MenuItems();
		private int currentPosition = 0;
		private int basePosition = 0;
		private IntPtr roolIntPtr;
		private bool allowMultiSelect;
		#endregion

		#region Private Methods

		private void GetParentMenu(MenuItems menuContainer, MenuItems parentMenu, ref MenuItems foundMenu)
		{
			foreach ( MenuItems mItems in menuContainer )
			{
				if ( mItems == parentMenu )
				{
					foundMenu = mItems;
				}
				else if ( mItems != null && mItems.Count > 0 )
				{
					GetParentMenu(mItems, parentMenu, ref foundMenu);
				}
			}
		}

		private void BuildMenus(Win32.HMenu parentMenu, MenuItems menuItems)
		{
			foreach ( MenuItems mItems in menuItems )
			{
				currentPosition++;
				if ( mItems.Count > 0 )
				{
					Win32.HMenu mMainContainer = Win32.CreatePopupMenu();
					mItems.Container = mMainContainer;
					BuildMenus(mMainContainer, mItems);
					Win32.InsertMenu(parentMenu, mItems.Location, Win32.MenuFlags.MF_BYPOSITION|Win32.MenuFlags.MF_POPUP, mMainContainer.handle, mItems.Text);
					if ( mItems.Icon != null )
					{
						Bitmap bmp = mItems.Icon;
						if ( mItems.IsIconTransparent )
						{
							Color backColor = bmp.GetPixel(1, 1);
							bmp.MakeTransparent(backColor);
						}
						Win32.SetMenuItemBitmaps(parentMenu, mItems.Location, Win32.MenuFlags.MF_BYPOSITION, bmp.GetHbitmap(), bmp.GetHbitmap());
					}
				}
				else
				{
					if ( roolIntPtr == parentMenu.handle )
					{
						Win32.HMenu mMainContainer = Win32.CreatePopupMenu();
						Win32.InsertMenu(parentMenu, mItems.Location, Win32.MenuFlags.MF_BYPOSITION, mMainContainer.handle, mItems.Text);
						mItems.Container = mMainContainer;
						if ( mItems.Icon != null )
						{
							Bitmap bmp = mItems.Icon;
							if ( mItems.IsIconTransparent )
							{
								Color backColor = bmp.GetPixel(1, 1);
								bmp.MakeTransparent(backColor);
							}
							Win32.SetMenuItemBitmaps(mItems.Parent.Container, mItems.Location, Win32.MenuFlags.MF_BYPOSITION, bmp.GetHbitmap(), bmp.GetHbitmap());
						}
					}
					else if ( mItems.IsSeperator )
					{
						Win32.AppendMenu(parentMenu, Win32.MenuFlags.MF_SEPARATOR, new IntPtr(mItems.Location), mItems.Text);
					}
					else
					{
						mItems.Container = parentMenu;
						Win32.AppendMenu(parentMenu, Win32.MenuFlags.MF_STRING, new IntPtr(mItems.Location+basePosition), mItems.Text);
						hsIDCommand.Add(mItems.Location, mItems);
						if ( mItems.Icon != null )
						{
							Bitmap bmp = mItems.Icon;
							if ( mItems.IsIconTransparent )
							{
								Color backColor = bmp.GetPixel(1, 1);
								bmp.MakeTransparent(backColor);
							}
							Win32.SetMenuItemBitmaps(parentMenu, mItems.Location + basePosition, Win32.MenuFlags.MF_STRING, bmp.GetHbitmap(), bmp.GetHbitmap());
						}
					}
				}
			}
		}	

		#endregion

		#region Public Properties

		/// <summary>
		/// Allow the selection of multiple files/folders etc. If false, the context menu wont be displayed
		/// while selecting multiple files
		/// </summary>
		public bool AllowMultiSelect
		{
			get
			{
				return allowMultiSelect;
			}
			set
			{
				allowMultiSelect = value;
			}
		}
		#endregion

		#region Public Methods

		/// <summary>
		/// This is the abstract method which should be implemented by the driven class.
		/// </summary>
		public abstract void AssembleMenu();

		/// <summary>
		/// Add context menus into the parent containers
		/// </summary>
		/// <param name="parentMenu">Parent menu</param>
		/// <param name="childMenu">Child menu</param>
		/// <returns>true if added</returns>
		public bool AddMenu(MenuItems parentMenu, MenuItems childMenu)
		{
			bool bResult = false;
			try
			{
				MenuItems parent = null;
				GetParentMenu(menus, parentMenu, ref parent);
				if ( parent == null )
				{
					MessageBox.Show("MenuItem","Parent Menu not found",MessageBoxButtons.OK, MessageBoxIcon.Error);
				}
				else
				{
					childMenu.Parent = parent;
					parent.Add(childMenu);
					bResult = true;
				}
			}
			catch(Exception ex)
			{
				MessageBox.Show("MenuItem",ex.Message,MessageBoxButtons.OK, MessageBoxIcon.Error);
			}
			return bResult;
		}

		/// <summary>
		/// Insert the menu to the root context menu which the shell has. This will be called before AddMenu
		/// </summary>
		/// <param name="menus"></param>
		/// <returns></returns>
		public bool InsertMenu(MenuItems menus)
		{
			bool bResult = false;
			this.menus.Add(menus);
			menus.Parent = this.menus;
			return bResult;
		}

		#endregion

		#region Constructor

		/// <summary>
		/// Constructor
		/// </summary>
		public BaseContextMenu()
		{
			drop = 0;
			AssembleMenu();
		}
        private string directory;
        public string Directory
        {
            get
            {
                return directory;
            }
            set
            {
                directory = value;
            }
        }

        private ArrayList parameters;
        public ArrayList Parameters
        {
            get
            {
                return parameters;
            }
            set
            {
                parameters = value;
            }
        }
		#endregion

		#region IContextMenu Members

		int Win32.IContextMenu.QueryContextMenu(Win32.HMenu hMenu, uint indexMenu, uint idCmdFirst, int idCmdLast, Win32.ContextMenuFlag uFlags)
		{
			basePosition = (int)idCmdFirst;
			if (((uFlags & (Win32.ContextMenuFlag.CMF_NOVERBS | Win32.ContextMenuFlag.CMF_DEFAULTONLY | Win32.ContextMenuFlag.CMF_VERBSONLY)) == 0 ) || ((uFlags & (Win32.ContextMenuFlag.CMF_NORMAL | Win32.ContextMenuFlag.CMF_EXPLORE)) != 0 ))
			{
				StringBuilder sbFile = new StringBuilder(1024);
				uint nSelected = Win32.DragQueryFile((IntPtr)drop, 0xffffffff, null, 0);
				roolIntPtr = hMenu.handle;
				if ((!AllowMultiSelect  && nSelected == 1) || (nSelected > 1 && AllowMultiSelect))
				{
					BuildMenus(hMenu, menus);
				}
			}
			return currentPosition;
		}

		void Win32.IContextMenu.GetCommandString(uint idCmd, uint uFlags, IntPtr pwReserved, IntPtr pszName, uint cchMax)
		{
			switch((Win32.GCSFlags)uFlags)
			{
				default:
					break;
			}
		}

		void Win32.IContextMenu.InvokeCommand(ref Win32.CMINVOKECOMMANDINFO pici)
		{
            StringBuilder sbFile = new StringBuilder(1024);
            uint nSelected = Win32.DragQueryFile((IntPtr)drop, 0, sbFile, (uint)sbFile.Capacity + 1);
            string object_name = sbFile.ToString();

            Directory = object_name;
            
            //*******************************************************//

            //StringBuilder sb = new StringBuilder(1024);
            //uint i = 0;
            ////Get number of files
            //uint uNumFiles = 0;
            ////IntPtr m_hDrop = 0;
            //uNumFiles = Win32.DragQueryFile((IntPtr)drop, 0xFFFFFFFF, null, 0);
            ////make file name 
            //while (i < uNumFiles)
            //{
            //    Win32.DragQueryFile((IntPtr)drop, i, sb, (uint)sb.Capacity + 1);
            //    //fileList.Append(sb + ",");
            //    parameters.Add(sb);
            //    i++;
            //}
            //*************************************************************//

			MenuItems items =  hsIDCommand[pici.verb] as MenuItems;
			if ( items!= null )
			{
				items.DoAction();
			}
		}

		#endregion

		#region IShellExtInit Members

		int Win32.IShellExtInit.Initialize(IntPtr pidlFolder, IntPtr lpdobj, uint hkeyProgID)
		{
			try
			{
				this.dataObject = null;
				if (lpdobj != IntPtr.Zero)
				{
					this.dataObject = Marshal.GetObjectForIUnknown(lpdobj) as Win32.IDataObject;
					Win32.FORMATETC formatetc = new Win32.FORMATETC();
					formatetc.cfFormat = Win32.CLIPFORMAT.CF_HDROP;
					formatetc.ptd = 0;
					formatetc.dwAspect = Win32.DVASPECT.DVASPECT_CONTENT;
					formatetc.lindex = -1;
					formatetc.tymed = Win32.TYMED.TYMED_HGLOBAL;
					Win32.STGMEDIUM stgmedium = new Win32.STGMEDIUM();
					this.dataObject.GetData(ref formatetc, ref stgmedium);
					this.drop = stgmedium.hGlobal;
				}
			}
			catch (Exception)
			{
			}
			return 0;
		}

		#endregion

	}
}
